<template>
  <tinymce-vue v-model="contentValue" :init="tinymceOptions" :id="id" />
</template>

<script setup lang="ts">
  import tinymce from 'tinymce'
  import TinymceVue from '@tinymce-plugin/tinymce-vue'
  import 'tinymce-plugin'
  import { defineProps, defineEmits } from 'vue'
  import { uploadPdfPlugin } from './plugins/pdf'
  import filePreviewPlugin from './plugins/filePreview/index'
  import {
    findAllImgSrcsFromHtml,
    extractImageDataFromRtf,
    replaceImagesFileSourceWithInlineRepresentation,
    _convertHexToBase64,
    base64ToBlob
  } from './plugins/parseWord/utils'
  import { ElLoading } from 'element-plus'
  import { ossSaveMinioFileUsingPost } from '@/api/4a/osswenjianshangchuan'
  import { useSettingStore } from '@/store/modules/setting'
  import { storeToRefs } from 'pinia'
  import { computed, watch } from 'vue'

  // 注册插件
  tinymce.PluginManager.add('uploadPdfPlugin', uploadPdfPlugin)
  tinymce.PluginManager.add('filePreview', filePreviewPlugin)

  const props = defineProps({
    modelValue: {
      type: String,
      required: true
    },
    height: {
      type: Number,
      default: 600
    },
    id: {
      type: [String, Number],
      default: 'mytinymce'
    }
  })

  const emit = defineEmits(['update:modelValue'])
  const settingStore = useSettingStore()
  const { isDark } = storeToRefs(settingStore)

  let contentValue = computed({
    get() {
      return props.modelValue
    },
    set(value) {
      emit('update:modelValue', value)
    }
  })

  const tinymceOptions = computed(() => ({
    language: 'zh_CN', // 设置语言为中文
    base_url: './tinymce', // tinymce静态资源基础路径
    skin: isDark.value ? 'oxide-dark' : 'oxide', // 根据主题设置皮肤
    content_css: isDark.value ? 'dark' : 'default', // 根据主题设置内容区域样式
    menubar: true, // 显示菜单栏
    branding: false, // 隐藏tinymce品牌
    min_height: props.height, // 最小高度
    max_height: props.height, // 最大高度
    media_live_embeds: true, // 允许嵌入媒体
    paste_word_valid_elements:
      '-strong/b,-em/i,-u,-span,-p,-ol,-ul,-li,-h1,-h2,-h3,-h4,-h5,-h6,-p/div,-a[href|name],sub,sup,strike,br,del,table[width],tr,td[colspan|rowspan|width],th[colspan|rowspan|width],thead,tfoot,tbody,img[width|height|alt|src]',
    file_picker_types: 'media', // 文件选择器类型
    block_unsupported_drop: true, // 阻止不支持的拖放
    powerpaste_word_import: 'clean', // word文档粘贴时清除格式
    powerpaste_html_import: 'clean', // html粘贴时清除格式
    powerpaste_allow_local_images: true, // 允许粘贴本地图片
    powerpaste_keep_unsupported_src: true, // 保留不支持的src属性
    placeholder: '在这里输入文字',
    paste_data_images: true, // 允许粘贴图片
    toolbar_sticky: false, // 工具栏不固定
    skeletonScreen: true, // 启用骨架屏
    autosave_ask_before_unload: false, // 关闭页面前不询问是否保存
    paste_preprocess: async (plugin, e) => {
      if (!e.__event.clipboardData) return true

      const html = e.__event.clipboardData.getData('text/html') // 获取粘贴的 html

      const text = e.__event.clipboardData
        .getData('text/plain')
        .replace(/@font-face{[^>]*div.Section0{page:Section0;}/g, '') // 获取粘贴的纯文本

      const rtf = e.__event.clipboardData.getData('text/rtf') // 获取 rtf 数据

      // 如果只有纯文本，直接返回true使用默认粘贴行为
      if (!html && text) {
        return true
      }

      try {
        // 处理html内容
        if (html) {
          console.log(html, 'html')

          // 图片加载loading
          const loading = ElLoading.service({
            text: '正在复制粘贴，请稍后...',
            background: 'rgba(0, 0, 0, 0.7)'
          })
          try {
            e.preventDefault() //阻止继续走粘贴，做完处理再手动将内容插入到编辑器
            // 列表缩进会超出边框，直接过滤掉
            let processedHtml = html.replace(/text-indent:-(.*?)pt/gi, '')

            // 从html内容中查找图片
            const imgSrcs = findAllImgSrcsFromHtml(processedHtml)

            // 如果有图片且有rtf数据
            if (imgSrcs && Array.isArray(imgSrcs) && imgSrcs.length && rtf) {
              const rtfImageData = extractImageDataFromRtf(rtf)

              if (rtfImageData.length) {
                // 将图片转换为base64
                processedHtml = replaceImagesFileSourceWithInlineRepresentation(
                  processedHtml,
                  imgSrcs,
                  rtfImageData
                )

                // 上传图片到服务器
                for (let i = 0; i < rtfImageData.length; i++) {
                  const base64Data = _convertHexToBase64(rtfImageData[i].hex)
                  const blob = base64ToBlob(base64Data, rtfImageData[i].type)
                  const file = new File(
                    [blob],
                    `paste_image_${i}.${rtfImageData[i].type.split('/')[1]}`,
                    {
                      type: rtfImageData[i].type
                    }
                  )

                  try {
                    const res = await ossSaveMinioFileUsingPost({
                      body: {},
                      file
                    })

                    if (res.code === 0) {
                      // 替换base64为服务器返回的url
                      processedHtml = processedHtml.replace(
                        `data:${rtfImageData[i].type};base64,${base64Data}`,
                        res.data.path
                      )
                    }
                  } catch (err) {
                    console.error('图片上传失败:', err)
                    ElMessage.error(`图片上传失败`)
                  }
                }

                e.target.insertContent(processedHtml)
                e.preventDefault()
                return false
              }
            }

            // 如果html中没有图片，直接插入处理后的html
            e.target.insertContent(processedHtml)
            e.preventDefault()
            return false
          } finally {
            loading.close()
          }
        }

        return true
      } catch (err) {
        console.error('粘贴处理出错:', err)
        return true
      }
    },
    plugins: [
      // 启用的插件列表
      'print axupimgs  preview insertdatetime searchreplace visualblocks paste',
      'autolink tpLayout fullscreen toc image upfile link media code codesample table charmap',
      'hr pagebreak anchor advlist lists textpattern help emoticons autosave tpIndent2em',
      'axupimgs tpLetterspacing imagetools quickbars attachment wordcount autoresize tpImportword uploadPdfPlugin filePreview'
    ],
    toolbar_mode: 'wrap', // 工具栏模式为自动换行
    toolbar_groups: {
      // 工具栏分组配置
      formatting: {
        text: '文字格式',
        tooltip: 'Formatting',
        items: 'bold italic underline | superscript subscript'
      },
      alignment: {
        icon: 'align-left',
        tooltip: 'alignment',
        items: 'alignleft aligncenter alignright alignjustify'
      }
    },
    toolbar: [
      // 工具栏按钮配置
      'undo redo | cut copy | searchreplace | fullscreen preview', // 常用操作放在最上方
      'formatselect fontselect fontsizeselect | bold italic underline strikethrough | forecolor backcolor', // 文字格式
      'alignment | tpIndent2em outdent indent | lineheight tpLetterspacing | bullist numlist | blockquote', // 段落格式
      'link image media axupimgs upfile attachment | table | emoticons charmap', // 插入功能
      'code codesample visualblocks | tpLayout | hr pagebreak | tpImportword uploadPdfPlugin filePreview', // 代码和布局
      'subscript superscript | removeformat | quickbars toc | help wordcount | restoredraft' // 其他功能
    ].join(' | '),
    fontsize_formats: '初号=56px 小初=48px 一号=34.7px 小一=32px 二号=29.3px 小二=24px 三号=21.3px 小三=20px 四号=18.7px 小四=16px 五号=14px 小五=12px 六号=10px 小六=8.7px 七号=7.3px 八号=6.7px', // 字号配置
    // 字体配置
    font_formats:
      '微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;' +
      '苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;' +
      '宋体=simsun,serif;' +
      '仿宋体=FangSong,serif;' +
      '黑体=SimHei,sans-serif;' +
      'Arial=arial,helvetica,sans-serif;' +
      'Symbol=symbol;' +
      'Tahoma=tahoma,arial,helvetica,sans-serif;' +
      'Terminal=terminal,monaco;' +
      'Times New Roman=times new roman,times;' +
      'Verdana=verdana,geneva;' +
      'Webdings=webdings;' +
      'Wingdings=wingdings,zapf dingbats;',
    // 图片上传处理
    images_upload_handler: function (blobInfo, succFun, failFun) {
      // 图片上传处理
      let file = blobInfo.blob()
      file = new File([file], 'vue-' + Date.now() + Math.floor(Math.random() * 1000) + '.png', {
        type: 'image/png'
      })
      // 这里您需要实现自己的文件上传逻辑
      ossSaveMinioFileUsingPost({
        body: {},
        file
      }).then((res) => {
        if (res.code === 0) {
          succFun(res.data.path)
        }
      })
    },
    // 视频播放插件
    media_url_resolver: function (data, resolve) {
      try {
        let videoUri = encodeURI(data.url)
        let embedHtml = `<p>
                      <span
                        class="mce-preview-object mce-object-video"
                        data-mce-selected="1"
                        data-mce-object="video"
                        data-mce-p-width="300px"
                        data-mce-p-height="150px"
                        data-mce-p-controls="controls"
                        data-mce-p-controlslist="nodownload"
                        data-mce-p-allowfullscreen="true"
                        data-mce-p-src=${videoUri} >
                        <video src=${data.url} width="300px" height="150px" controls="controls" controlslist="nodownload">
                        </video>
                      </span>
                    </p>
                    <p style="text-align: left;"></p>`
        resolve({ html: embedHtml })
      } catch (e) {
        resolve({ html: '' })
      }
    },
    //适用于视频播放插件
    file_picker_callback: function (succFun, value, meta) {
      const filetype =
        '.pdf, .txt, .zip, .rar, .7z, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .mp3, .mp4'
      const input = document.createElement('input')
      input.setAttribute('type', 'file')
      input.setAttribute('accept', filetype)
      input.click()
      input.onchange = function () {
        const file = this.files[0]

        // 这里您需要实现自己的文件上传逻辑
        ossSaveMinioFileUsingPost({
          body: {},
          file
        }).then((res) => {
          if (res.code === 0) {
            succFun(res.data.path, { text: file.name })
          }
        })
      }
    },
    //适用于上传文件插件
    file_callback: function (file, succFun) {
      // 文件上传回调
      //文件上传  file:文件对象 succFun(url|string,obj) 成功回调
      let data = new FormData()
      data.append('file', file)
      ossSaveMinioFileUsingPost({
        body: {},
        file
      }).then((res) => {
        if (res.code === 0) {
          succFun(res.data.path, { text: res.data.name })
        }
      })
    },
    attachment_max_size: 209715200,
    attachment_style: '.attachment>img{display:inline-block!important;max-width:30px!important;}',
    attachment_icons_path: 'https://unpkg.com/@npkg/tinymce-plugins/plugins/attachment/icons',
    attachment_upload_handler: function (file, succFun, failFun, progressCallback) {
      ossSaveMinioFileUsingPost({
        body: {},
        file
      }).then((res) => {
        if (res.code === 0) {
          succFun(res.data.path)
        }
      })
    }
  }))

  // 监听主题变化，重新加载编辑器
  watch(isDark, () => {
    // 获取编辑器实例
    const editor = tinymce.get(props.id)
    if (editor) {
      // 保存当前内容
      const content = editor.getContent()
      // 移除编辑器
      editor.remove()
      // 等待DOM更新
      setTimeout(() => {
        // 重新初始化编辑器
        tinymce.init({
          ...tinymceOptions.value,
          selector: `#${props.id}`,
          setup: (editor) => {
            editor.on('init', () => {
              editor.setContent(content)
            })
          }
        })
      }, 0)
    }
  })
</script>

<style scoped></style>
